"""
Grave! Important! Важно!

Proletoj el ĉiuj landoj, unuiĝu!
Workers of the world, unite!
Пролетарии всех стран, соединяйтесь!

https://tkom.pro
"""

from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import Permission
from django.db.models import Q
from siriuso.utils import get_user_by_token
from main.models import Uzanto, SpecialajGrupoj


class UzantoAuthBackend:
    def authenticate(self, request, username=None, password=None):
        if username and password:
            try:
                user = Uzanto.objects.filter(Q(chefa_retposhto=username) | Q(chefa_telefonanumero=username))

                if not user.count():
                    raise Uzanto.DoesNotExist

                user = user[0]

                if check_password(password, user.password) and self.user_can_authenticate(user):
                    return user
            except Uzanto.DoesNotExist:
                return None
        return None

    def user_can_authenticate(self, user):
        is_active = getattr(user, 'is_active', None)
        return is_active or is_active is None

    def _get_user_permissions(self, user_obj):
        return user_obj.user_permissions.all()

    def _get_group_permissions(self, user_obj):
        perms = Permission.objects.none()
        groups = user_obj.groups.all()

        try:
            groups |= SpecialajGrupoj.objects.get(kodo='registrita').grupoj.all()
        except SpecialajGrupoj.DoesNotExist:
            pass

        for group in groups:
            perms |= group.permissions.all()

        return perms

    def _get_permissions(self, user_obj, obj, from_name):
        """
        Return the permissions of `user_obj` from `from_name`. `from_name` can
        be either "group" or "user" to return permissions from
        `_get_group_permissions` or `_get_user_permissions` respectively.
        """
        obj_attr_name = '_get_%s_permissions' % from_name

        # Парсим права для неавторизированных пользователей через спец. группы
        if user_obj.is_anonymous:
            try:
                perms_groups = SpecialajGrupoj.objects.get(kodo='neregistrita')
                perms = set()

                for group in perms_groups.grupoj.all():
                    group_perms = group.permissions.values_list('content_type__app_label', 'codename').order_by()
                    perms = perms | {"%s.%s" % (ct, name) for ct, name in group_perms}

                return perms
            except SpecialajGrupoj.DoesNotExist:
                pass

            return set()

        # Если пользователь заблокирован или объект не имеет метода получения прав на него
        if not user_obj.is_active or (obj is not None and not hasattr(obj, obj_attr_name)):
            return set()

        obj_key = None if obj is None else "{}.{}.{}".format(obj._meta.app_label, obj._meta.model_name, obj.pk)
        perm_cache_name = '_%s_perm_cache' % from_name if obj is None else '_%s_obj_perm_cache' % from_name

        if not hasattr(user_obj, perm_cache_name):
            if obj is not None:
                # Вычисляем права для объекта
                perms = (getattr(obj, obj_attr_name)(user_obj)
                         .values_list('content_type__app_label', 'codename').order_by())

                setattr(user_obj, perm_cache_name, {})
                obj_cache = getattr(user_obj, perm_cache_name)
                obj_cache[obj_key] = {"%s.%s" % (ct, name) for ct, name in perms}
            else:
                if user_obj.is_superuser:
                    perms = Permission.objects.all()
                else:
                    perms = getattr(self, obj_attr_name)(user_obj)

                perms = perms.values_list('content_type__app_label', 'codename').order_by()
                setattr(user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms})
        elif obj_key and obj_key not in getattr(user_obj, perm_cache_name):
            perms = (getattr(obj, obj_attr_name)(user_obj)
                     .values_list('content_type__app_label', 'codename').order_by())

            obj_cache = getattr(user_obj, perm_cache_name)
            obj_cache[obj_key] = set()
            obj_cache[obj_key].update({"%s.%s" % (ct, name) for ct, name in perms})

        return getattr(user_obj, perm_cache_name) if obj_key is None else getattr(user_obj, perm_cache_name)[obj_key]

    def get_user_permissions(self, user_obj, obj=None):
        """
        Return a set of permission strings the user `user_obj` has from their
        `user_permissions`.
        """
        return self._get_permissions(user_obj, obj, 'user')

    def get_group_permissions(self, user_obj, obj=None):
        """
        Return a set of permission strings the user `user_obj` has from the
        groups they belong.
        """
        return self._get_permissions(user_obj, obj, 'group')

    def get_all_permissions(self, user_obj, obj=None):
        perm_cache_name = '_perm_cache' if obj is None else '_obj_perm_cache'
        obj_key = None if obj is None else "{}.{}.{}".format(obj._meta.app_label, obj._meta.model_name, obj.pk)

        if not hasattr(user_obj, perm_cache_name):
            if obj is None:
                setattr(user_obj, perm_cache_name, set())
                getattr(user_obj, perm_cache_name).update(self.get_user_permissions(user_obj))
                getattr(user_obj, perm_cache_name).update(self.get_group_permissions(user_obj))
            else:
                setattr(user_obj, perm_cache_name, {})
                obj_cache = getattr(user_obj, perm_cache_name)
                obj_cache[obj_key] = set()
                obj_cache[obj_key].update(self.get_user_permissions(user_obj, obj))
                obj_cache[obj_key].update(self.get_group_permissions(user_obj, obj))
        elif obj_key and obj_key not in user_obj._obj_perm_cache:
            user_obj._obj_perm_cache[obj_key] = set()
            user_obj._obj_perm_cache[obj_key].update(self.get_user_permissions(user_obj, obj))
            user_obj._obj_perm_cache[obj_key].update(self.get_group_permissions(user_obj, obj))

        return user_obj._perm_cache if obj_key is None else user_obj._obj_perm_cache.get(obj_key)

    def has_perm(self, user_obj, perm, obj=None):
        return perm in self.get_all_permissions(user_obj, obj)

    def has_module_perms(self, user_obj, app_label):
        """
        Return True if user_obj has any permissions in the given app_label.
        """
        if not user_obj.is_active:
            return False
        for perm in self.get_all_permissions(user_obj):
            if perm[:perm.index('.')] == app_label:
                return True
        return False

    def get_user(self, user_id):
        try:
            uzanto = Uzanto.objects.get(pk=user_id)
        except Uzanto.DoesNotExist:
            return None

        return uzanto if self.user_can_authenticate(uzanto) else None


class TokenAuthBackend:
    """
    Позволяет авторизироваться за счет ключа,
    переданного в заголовке X-AUTH-TOKEN HTTP
    запроса
    """
    def authenticate(self, request):
        uzanto = get_user_by_token(request)
        uzanto = uzanto if uzanto and self.user_can_authenticate(uzanto) else None
        return uzanto

    def user_can_authenticate(self, user):
        is_active = getattr(user, 'is_active', None)
        return is_active or is_active is None

    def get_user(self, user_id):
        try:
            uzanto = Uzanto.objects.get(pk=user_id)
        except Uzanto.DoesNotExist:
            return None

        return uzanto if self.user_can_authenticate(uzanto) else None
